package com.fushenlan.epidemicControl.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.fushenlan.commons.ApiGlobalErrorDefinition;
import com.fushenlan.commons.CheckParamUtil;
import com.fushenlan.commons.GeneralResponse;
import com.fushenlan.epidemicControl.annotation.RequestLimit;
import com.fushenlan.epidemicControl.config.JwtProvider;
import com.fushenlan.epidemicControl.config.JwtUtil;
import com.fushenlan.epidemicControl.dto.LoginReqDTO;
import com.fushenlan.epidemicControl.dto.UserChangeMeDTO;
import com.fushenlan.epidemicControl.service.NeighborhoodMemberService;
import com.fushenlan.po.NeighborhoodMemberDO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Slf4j
@RestController
@RequestMapping("/neighborhood/user")
@Api(value = "用户信息管理", tags = "user-controller")
public class LoginUserController {
    @Autowired
    private NeighborhoodMemberService neighborhoodMemberService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private JwtProvider jwtProvider;

    @Autowired
    HttpServletRequest request;

    private static final String VERIFICATION_CODE = "VERIFICATION_CODE:";
    private static final String JWT_SIGN_KEY = "WorldPeace!ideje2edLdiald5@ddkld";
    private static final String REGEX_PATTERN = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%*#?&])[A-Za-z\\d$@$!%*#?&]{6,16}$";

    @RequestMapping("/login")
    @ApiOperation(value = "登录", notes = "登录")
    public GeneralResponse<?> login(@RequestBody LoginReqDTO loginReqDTO) {
        String s = CheckParamUtil.checkNull(loginReqDTO, Arrays.asList("account", "password", "verificationCode"));
        if (StrUtil.isNotBlank(s)) {
            return GeneralResponse.fail("-1", s, "bad request!");
        }
        //验证验证码是否有效
        String code = redisTemplate.opsForValue().get(VERIFICATION_CODE + loginReqDTO.getVerificationCode());
        if (StrUtil.isBlank(code)) {
            return GeneralResponse.fail("-1", "验证码已过期", "bad request!");
        }
        if (!code.equals(loginReqDTO.getVerificationCode())) {
            return GeneralResponse.fail("-1", "验证码输入错误", "bad request!");
        }
        //根据用户名和密码查询后台是否存在，存在则登录成功
        String md5Pwd = SecureUtil.md5(loginReqDTO.getPassword());
        log.info("MD5加密后的密码：{}", md5Pwd);
        Map<String, Object> map = neighborhoodMemberService.findUser(loginReqDTO.getAccount());
        if (CollUtil.isEmpty(map)) {
            return GeneralResponse.fail("-1", "账号不存在", "");
        }

        map.put("userId", map.get("id"));
        //生成token，用以校验下次登录
        String token = jwtProvider.createToken(map);

        //获取数据库中的密码
        Object password = map.get("password");
        if (password == null || "".equals(password)) {
            //未设置密码，提示不予登录
            return GeneralResponse.fail("-1", "请联系管理员后台先设置初始密码", "");
        }
        //先直接比较密码
        if (loginReqDTO.getPassword().equals(password)) {
            GeneralResponse<Object> generalResponse = new GeneralResponse<>("0000", "温馨提示：密码为初始密码，请及时修改", "");
            generalResponse.setData(token);
            return generalResponse;
        } else {
            //再根据MD5加密后比较
            if (!md5Pwd.equals(password)) {
                return GeneralResponse.fail("-1", "用户名或密码不正确", "");
            }
        }
        return GeneralResponse.success(token);
    }


    @RequestMapping("/changePwd")
    @ApiOperation(value = "修改密码", notes = "修改密码")
    public GeneralResponse<?> getUserById(@RequestBody UserChangeMeDTO userChangeMeDTO) {
        String s = CheckParamUtil.checkNull(userChangeMeDTO, Arrays.asList("originalPassword", "newPassword", "confirmPassword"));
        if (StrUtil.isNotBlank(s)) {
            return GeneralResponse.fail("-1", s, "bad request!");
        }
        //解析token获取登录用户信息
        String userId = JwtUtil.getUserInfo("userId");
        log.info("resolve token userId={}", userId);
        if (StrUtil.isBlank(userId)) {
            return GeneralResponse.fail("-1", "用户不存在", "用户id不能为空");
        }
        //校验1：原密码必须与系统记录一致，否则提示“原密码输入错误”
        //密码都需要MD5加密，判断需不需要验证初始密码
        String md5OriginalPwd = SecureUtil.md5(userChangeMeDTO.getOriginalPassword());
        NeighborhoodMemberDO userByPwd = neighborhoodMemberService.findUserByPwd(Long.valueOf(userId), userChangeMeDTO.getOriginalPassword(), md5OriginalPwd);
        if (userByPwd == null) {
            return GeneralResponse.fail("-1", "原密码输入错误", "bad request!");
        }
        //验证新密码是否符合密码策略
        if (!Pattern.matches(REGEX_PATTERN, userChangeMeDTO.getNewPassword())) {
            return GeneralResponse.fail("-1", "密码格式不正确，请输入6-16位数字、字母和特殊字符@$!%*#?&的组合", "bad request!");
        }
        if (!userChangeMeDTO.getNewPassword().equals(userChangeMeDTO.getConfirmPassword())) {
            return GeneralResponse.fail("-1", "确认密码和新密码不一致", "bad request!");
        }
        //验证新密码是否和原密码一致
        if (userChangeMeDTO.getNewPassword().equals(userChangeMeDTO.getOriginalPassword())) {
            return GeneralResponse.fail("-1", "新密码不能和原密码相同", "bad request!");
        }
        String md5NewPwd = SecureUtil.md5(userChangeMeDTO.getNewPassword());
        if (userChangeMeDTO.getNewPassword().equals(userByPwd.getPassword()) || md5NewPwd.equals(userByPwd.getPassword())) {
            return GeneralResponse.fail("-1", "新密码不能和原密码相同", "bad request!");
        }
        //验证通过，更新密码
        userByPwd.setPassword(md5NewPwd);
        userByPwd.setUpdateDate(DateUtil.now());
        try {
            neighborhoodMemberService.update(userByPwd);
        } catch (Exception e) {
            log.error("修改密码失败", e);
            return GeneralResponse.fail("-1", "修改密码失败", "");
        }
        //将当前的access_token加入黑名单
        JWTPayload payload = null;
        try {
            payload = getPayload();
        } catch (Exception e) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.TOKEN_INVALID);
        }
        String expireTime = payload.getClaim(JWTPayload.EXPIRES_AT).toString();
        String jwtId = payload.getClaim(JWTPayload.JWT_ID).toString();
        //将当前的access_token加入黑名单
        long blackExpire = DateUtil.parseDateTime(expireTime).getTime() - DateUtil.date().getTime();
        redisTemplate.opsForValue().set("access_token:" + jwtId, userByPwd.getId() + "", blackExpire / 1000, TimeUnit.SECONDS);
        return GeneralResponse.success();
    }

    @RequestMapping("/loginOut")
    @ApiOperation(value = "登出", notes = "登出")
    public GeneralResponse<?> loginOut() {
        JWTPayload payload = null;
        try {
            payload = getPayload();
        } catch (Exception e) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.TOKEN_INVALID);
        }
        String expireTime = payload.getClaim(JWTPayload.EXPIRES_AT).toString();
        String jwtId = payload.getClaim(JWTPayload.JWT_ID).toString();
        String userId = payload.getClaim("userId") + "";
        //将当前的access_token加入黑名单
        long blackExpire = DateUtil.parseDateTime(expireTime).getTime() - DateUtil.date().getTime();
        redisTemplate.opsForValue().set("access_token:" + jwtId, userId, blackExpire / 1000, TimeUnit.SECONDS);
        return GeneralResponse.success();
    }


    @GetMapping("/getVerificationCode")
    @ApiOperation(value = "获取随机验证码", notes = "获取随机验证码")
    @RequestLimit(second = 1, maxCount = 1)
    public GeneralResponse<?> getVerificationCode() {
        // 接口防刷 自定义注解@RequestLimit(second = 1,maxCount = 1) 表示一秒内只能调用一次
        String randomCode = RandomUtil.randomNumbers(4);
        redisTemplate.opsForValue().set(VERIFICATION_CODE + randomCode, randomCode, 1L, TimeUnit.MINUTES);
        return GeneralResponse.success(randomCode);
    }

    private JWTPayload getPayload() {
        String token = jwtProvider.resolveToken(request);
        log.info("token:{}", token);
        boolean validateToken = jwtProvider.validateToken(token);
        if (!validateToken) {
            throw new RuntimeException("invalid token");
        }
        return JWTUtil.parseToken(token).getPayload();
    }
}